package com.tangtang.core;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.beanutils.BeanUtils;

import com.tangtang.core.log.LogUtil;

/**
 * @author yue
 *         打印出对象的所有属性的工具类
 */
public class BeanDebugUtil {

    @SuppressWarnings("restriction")
    static String lineSeparator = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator"));

    /**
     * 输出到日志文件
     *
     * @param bean
     */
    public static void dumpToLog(Object bean) {
        String message = dumpToString(bean);
        LogUtil.fatal(message);
    }

    /**
     * 打印对象
     *
     * @param bean
     */
    public static String dumpToString(Object bean) {
        StringBuilder builder = new StringBuilder();
        dump(bean, null, builder);
        return builder.toString();
    }

    /**
     * 调试, 打印出给定 Bean 的所有属性的取值.
     *
     * @param bean
     * @param proArray 需要打印的对象
     */
    private static void dump(Object bean, String[] proArray, StringBuilder builder) {
        if (bean instanceof Collection) {
            @SuppressWarnings("unchecked")
            Collection<Object> collection = (Collection<Object>) bean;
            builder.append(lineSeparator + "print Collection begein!");
            for (Object o : collection) {
                dump(o, proArray, builder);
            }
            builder.append(lineSeparator + "print Collection end!");
        } else if (bean instanceof Map) {
            builder.append(lineSeparator + "print Map begein!");
            @SuppressWarnings("unchecked")
            Map<Object, Object> map = (Map<Object, Object>) bean;
            for (Object o : map.keySet()) {
                Object value = map.get(o);
                if (isImmutableObjects(value)) {
                    builder.append("[" + o.toString() + "]=" + value.toString());
                } else {
                    builder.append(lineSeparator + "[" + o.toString() + "] begin");
                    dump(value, proArray, builder);
                    builder.append(lineSeparator + "[" + o.toString() + "] end!");
                }
            }
            builder.append("print Map end!");
        } else {
            if (isImmutableObjects(bean)) {
                // 不可变类
                builder.append(lineSeparator + "[" + bean.getClass().getName() + "]=" + bean.toString());
            } else {
                printObject(bean, proArray, builder);
            }
        }
    }

    /**
     * 判断是否是不可变类
     *
     * @param bean
     * @return
     */
    private static boolean isImmutableObjects(Object bean) {
        if (bean instanceof Integer || bean instanceof Long || bean instanceof String || bean instanceof Short || bean instanceof Boolean
                || bean instanceof Byte || bean instanceof Character || bean instanceof Double || bean instanceof Float || bean instanceof Number) {
            return true;
        }
        return false;
    }

    /**
     * 从 bean 中读取有效的属性描述符. NOTE: 名称为 class 的 PropertyDescriptor 被排除在外.
     *
     * @param bean Object - 需要读取的 Bean
     * @return PropertyDescriptor[] - 属性列表
     */
    private static java.beans.PropertyDescriptor[] getAvailablePropertyDescriptors(Object bean) {
        try {
            // 从 Bean 中解析属性信息并查找相关的 write 方法
            java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean.getClass());
            if (info != null) {
                java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
                Vector<PropertyDescriptor> columns = new Vector<PropertyDescriptor>();
                for (int i = 0; i < pd.length; i++) {
                    String fieldName = pd[i].getName();
                    if (fieldName != null && !fieldName.equals("class")) {
                        columns.add(pd[i]);
                    }
                }
                java.beans.PropertyDescriptor[] arrays = new java.beans.PropertyDescriptor[columns.size()];
                for (int j = 0; j < columns.size(); j++) {
                    arrays[j] = (PropertyDescriptor) columns.get(j);
                }
                return arrays;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
        return null;

    }

    private static void printObject(Object bean, String[] proArray, StringBuilder builder) {
        java.beans.PropertyDescriptor[] descriptors = getAvailablePropertyDescriptors(bean);
        for (int i = 0; descriptors != null && i < descriptors.length; i++) {
            java.lang.reflect.Method readMethod = descriptors[i].getReadMethod();
            try {
                String proName = descriptors[i].getName();
                if (proArray == null || (proArray != null && inArray(proName, proArray))) {
                    Object value = readMethod.invoke(bean, new Object[]{});
                    builder.append(lineSeparator + "[" + bean.getClass().getName() + "]." + proName + "(" + descriptors[i].getPropertyType().getName() + ") = "
                            + value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 判断proName是否在proArray里面
     *
     * @param proName
     * @param proArray
     * @return
     */
    private static boolean inArray(String proName, String[] proArray) {
        if (proArray == null)
            return false;
        boolean in = false;
        for (String s : proArray) {
            if (proName.equals(s)) {
                in = true;
                break;
            }
        }
        return in;
    }

    public static String toString(Object bean) {
        if (bean == null) {
            return "";
        }
        try {
            Map<?, ?> describe = BeanUtils.describe(bean);
            return describe.toString();
        } catch (IllegalAccessException e1) {
        } catch (InvocationTargetException e1) {
        } catch (NoSuchMethodException e1) {
        }
        return bean.toString();
    }
}
